﻿/*
 * @author: wizardc
 */

using UnityEngine;

namespace Dou.Controller
{
    /// <summary>
    /// 固定机位的摄像机工具类
    /// </summary>
    public static class FixedCameraUtil
    {
        /// <summary>
        /// 通过屏幕坐标点得到对应的 y=0 的地面坐标点
        /// </summary>
        public static Vector3 ScreenPointToGroundPoint(Camera camera, float x, float y)
        {
            var ray = camera.ScreenPointToRay(new Vector3(x, y));
            var dir = ray.direction / ray.direction.y;
            var move = ray.origin.y * dir;
            var point = ray.origin - move;
            point.y = 0;
            return point;
        }

        /// <summary>
        /// 将地面坐标点（xz表示地面坐标，y表示摄像机高度位置）转换为摄像机位置
        /// </summary>
        public static Vector3 GetGroundToCameraPosition(Camera camera, float x, float z, float y)
        {
            var forward = camera.transform.forward;
            var pos = new Vector3(x, 0, z);
            var dir = forward / forward.y;
            var move = y * dir;
            pos += move;
            pos.y = y;
            return pos;
        }

        /// <summary>
        /// 计算 Viewport
        /// </summary>
        public static void CalculateViewport(Camera camera, Vector3[] viewportPos)
        {
            // 矩形/梯形左上角
            viewportPos[0] = ScreenPointToGroundPoint(camera, 0, Screen.height);
            // 矩形/梯形右上角
            viewportPos[1] = ScreenPointToGroundPoint(camera, Screen.width, Screen.height);
            // 梯形左下角
            viewportPos[2] = ScreenPointToGroundPoint(camera, 0, 0);
            // 梯形右下角
            viewportPos[3] = ScreenPointToGroundPoint(camera, Screen.width, 0);
            // 矩形左下角
            viewportPos[4].Set(viewportPos[0].x, 0, viewportPos[2].z);
            // 矩形右下角
            viewportPos[5].Set(viewportPos[1].x, 0, viewportPos[3].z);
            // 屏幕中心点
            viewportPos[6] = ScreenPointToGroundPoint(camera, Screen.width / 2f, Screen.height / 2f);
            // 空间中心点
            viewportPos[7] = (viewportPos[0] + viewportPos[5]) / 2f;
        }

        /// <summary>
        /// 检测摄像机位置并限制在矩形范围内
        /// </summary>
        public static Vector3 RectLimit(Vector3[] viewportPos, Vector3 min, Vector3 max)
        {
            var screenCenterPos = viewportPos[6];
            
            // 矩形限制只需要判断视口矩形框即可
            var topLeft = viewportPos[0];
            var bottomRight = viewportPos[5];
            
            // 上
            if (topLeft.z > max.z)
            {
                screenCenterPos.z -= topLeft.z - max.z;
            }
            // 下
            if (bottomRight.z < min.z)
            {
                screenCenterPos.z += min.z - bottomRight.z;
            }
            // 左
            if (topLeft.x < min.x)
            {
                screenCenterPos.x += min.x - topLeft.x;
            }
            // 右
            if (bottomRight.x > max.x)
            {
                screenCenterPos.x -= bottomRight.x - max.x;
            }

            return screenCenterPos;
        }

        /// <summary>
        /// 检测摄像机位置并限制在梯形范围内
        /// </summary>
        public static Vector3 TrapezoidLimit(Vector3[] viewportPos, Vector3 topLeft, Vector3 topRight, Vector3 bottomLeft, Vector3 bottomRight)
        {
            var screenCenterPos = viewportPos[6];
            
            // 梯形限制需要检查视口梯形的每个顶点
            var vpTopLeft = viewportPos[0];
            var vpTopRight = viewportPos[1];
            var vpBottomLeft = viewportPos[2];
            var vpBottomRight = viewportPos[3];

            // 上
            if (vpTopLeft.z > topRight.z)
            {
                screenCenterPos.z -= vpTopLeft.z - topRight.z;
            }
            // 下
            if (vpBottomRight.z < bottomLeft.z)
            {
                screenCenterPos.z += bottomLeft.z - vpBottomRight.z;
            }
            // 左
            if (vpTopLeft.x < Mathf.Max(topLeft.x, bottomLeft.x))
            {
                var ratio = (vpTopLeft.z - bottomLeft.z) / (topLeft.z - bottomLeft.z);
                var limitX = (topLeft.x - bottomLeft.x) * ratio + bottomLeft.x;
                if (vpTopLeft.x < limitX)
                {
                    screenCenterPos.x += limitX - vpTopLeft.x;
                }
            }
            if (vpBottomLeft.x < Mathf.Max(topLeft.x, bottomLeft.x))
            {
                var ratio = (vpBottomLeft.z - bottomLeft.z) / (topLeft.z - bottomLeft.z);
                var limitX = (topLeft.x - bottomLeft.x) * ratio + bottomLeft.x;
                if (vpBottomLeft.x < limitX)
                {
                    screenCenterPos.x += limitX - vpBottomLeft.x;
                }
            }
            // 右
            if (vpTopRight.x > Mathf.Min(topRight.x, bottomRight.x))
            {
                var ratio = (vpTopRight.z - bottomRight.z) / (topRight.z - bottomRight.z);
                var limitX = (topRight.x - bottomRight.x) * ratio + bottomRight.x;
                if (vpTopRight.x > limitX)
                {
                    screenCenterPos.x -= vpTopRight.x - limitX;
                }
            }
            if (vpBottomRight.x > Mathf.Min(topRight.x, bottomRight.x))
            {
                var ratio = (vpBottomRight.z - bottomRight.z) / (topRight.z - bottomRight.z);
                var limitX = (topRight.x - bottomRight.x) * ratio + bottomRight.x;
                if (vpBottomRight.x > limitX)
                {
                    screenCenterPos.x -= vpBottomRight.x - limitX;
                }
            }

            return screenCenterPos;
        }
    }
}
